<?php

declare(strict_types=1);

namespace app\service\notify;

use app\model\NotifyMessage;
use app\model\NotifyTemplate;
use InvalidArgumentException;

class MessageService
{
    public function __construct(
        private CreateNotifyService $createNotifyService,
        private RecordService $recordService
    ) {
    }

    public function create(array $payload): NotifyMessage
    {
        $targets = $this->normaliseTargets($payload['recipients'] ?? null);
        if ($targets === []) {
            throw new InvalidArgumentException('通知接收者不能为空');
        }

        $data = $this->normaliseAssoc($payload['data'] ?? null);
        $extraPayload = $this->normaliseAssoc($payload['payload'] ?? null);

        $templateId = $payload['template_id'] ?? null;
        $templateType = trim((string) ($payload['template_type'] ?? ''));
        $template = null;

        if ($templateId !== null && $templateId !== '') {
            $template = NotifyTemplate::find((int) $templateId);
            if (!$template) {
                throw new InvalidArgumentException('模板不存在');
            }
            $templateType = (string) $template->type;
        }

        $result = null;
        if ($templateType !== '') {
            $options = [
                'payload' => $extraPayload,
            ];
            if ($template) {
                $options['template_id'] = (int) $template->id;
            }
            if (!empty($payload['title'])) {
                $options['title'] = (string) $payload['title'];
            }
            if (!empty($payload['content'])) {
                $options['content'] = (string) $payload['content'];
            }

            $result = $this->createNotifyService->add($targets, $templateType, $data, $options);
        } else {
            $title = trim((string) ($payload['title'] ?? ''));
            $content = (string) ($payload['content'] ?? '');

            if ($title === '' || $content === '') {
                throw new InvalidArgumentException('标题和内容不能为空');
            }

            $messagePayload = array_merge($data, $extraPayload);
            if (isset($payload['route']) && is_string($payload['route']) && $payload['route'] !== '') {
                $messagePayload['route'] = $payload['route'];
            }

            $result = $this->createNotifyService->addManual(
                $targets,
                $title,
                $content,
                $messagePayload,
                $templateId ? (int) $templateId : null
            );
        }

        $message = $result['message'] ?? null;
        if ($message instanceof NotifyMessage) {
            $messageId = (int) $message->id;
        } elseif (is_array($message) && isset($message['id'])) {
            $messageId = (int) $message['id'];
        } else {
            throw new InvalidArgumentException('通知创建失败');
        }

        $loaded = $this->recordService->findById($messageId);

        return $loaded ?? NotifyMessage::find($messageId);
    }

    private function normaliseTargets($targets): array
    {
        if (!is_array($targets)) {
            return [];
        }

        $results = [];
        foreach ($targets as $target) {
            if (!is_array($target)) {
                continue;
            }

            $id = $target['target_id'] ?? $target['id'] ?? null;
            $type = $target['target_type'] ?? $target['type'] ?? null;
            if ($id === null || $type === null) {
                continue;
            }

            if (is_string($id) && ctype_digit($id)) {
                $id = (int) $id;
            }

            if (!is_int($id)) {
                continue;
            }

            if ($id <= 0) {
                continue;
            }

            $channel = (string) ($target['channel'] ?? 'websocket');

            $results[] = [
                'id' => $id,
                'type' => (string) $type,
                'channel' => $channel !== '' ? $channel : 'websocket',
            ];
        }

        return $results;
    }

    private function normaliseAssoc($value): array
    {
        return is_array($value) ? $value : [];
    }
}
